package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"slices"
	"strings"

	"github.com/go-json-experiment/json"
	"github.com/go-json-experiment/json/jsontext"
	"google.golang.org/api/androidmanagement/v1"
	"google.golang.org/api/option"
)

// Required env vars:
var (
	androidServiceCredentials = os.Getenv("FLEET_DEV_ANDROID_GOOGLE_SERVICE_CREDENTIALS")
	androidProjectID          string
)

const (
	cmdEnterprisesDelete          = "enterprises.delete"
	cmdEnterprisesList            = "enterprises.list"
	cmdEnterprisesWebTokensCreate = "enterprises.webTokens.create"
	cmdApplicationsGet            = "applications.get"
	cmdPoliciesList               = "policies.list"
	cmdPoliciesDelete             = "policies.delete"
	cmdDevicesList                = "devices.list"
	cmdDevicesDelete              = "devices.delete"
	cmdDevicesRelinquish          = "devices.issueCommand.RELINQUISH_OWNERSHIP"
)

var commands = []string{
	cmdEnterprisesDelete,
	cmdEnterprisesList,
	cmdEnterprisesWebTokensCreate,
	cmdApplicationsGet,
	cmdPoliciesList,
	cmdPoliciesDelete,
	cmdDevicesList,
	cmdDevicesDelete,
	cmdDevicesRelinquish,
}

func main() {
	if androidServiceCredentials == "" {
		log.Fatal("FLEET_DEV_ANDROID_GOOGLE_SERVICE_CREDENTIALS must be set")
	}

	type credentials struct {
		ProjectID string `json:"project_id"`
	}
	var creds credentials
	err := json.Unmarshal([]byte(androidServiceCredentials), &creds)
	if err != nil {
		log.Fatalf("unmarshaling android service credentials: %s", err)
	}
	androidProjectID = creds.ProjectID
	if androidProjectID == "" {
		log.Fatal("project_id not found in android service credentials")
	}

	command := flag.String("command", "", strings.Join(commands, "\n"))
	enterpriseID := flag.String("enterprise_id", "", "")
	deviceID := flag.String("device_id", "", "")
	policyID := flag.String("policy_id", "", "")
	flag.Parse()

	if !slices.Contains(commands, *command) {
		flag.Usage()
		os.Exit(1)
	}

	// Normalize enterprise_id by stripping "enterprises/" prefix if present
	if *enterpriseID != "" {
		*enterpriseID = strings.TrimPrefix(*enterpriseID, "enterprises/")
	}

	if slices.Index(commands, *command) == -1 {
		log.Fatalf("Command must be one of: %s", strings.Join(commands, ", "))
	}

	ctx := context.Background()
	mgmt, err := androidmanagement.NewService(ctx, option.WithCredentialsJSON([]byte(androidServiceCredentials)))
	if err != nil {
		log.Fatalf("Error creating android management service: %v", err)
	}

	switch *command {
	case cmdEnterprisesDelete:
		enterprisesDelete(mgmt, *enterpriseID)
	case cmdEnterprisesList:
		enterprisesList(mgmt)
	case cmdEnterprisesWebTokensCreate:
		enterprisesWebTokensCreate(mgmt, *enterpriseID)
	case cmdApplicationsGet:
		applicationsGet(mgmt, *enterpriseID, flag.Arg(0))
	case cmdPoliciesList:
		policiesList(mgmt, *enterpriseID)
	case cmdPoliciesDelete:
		policiesDelete(mgmt, *enterpriseID, *policyID)
	case cmdDevicesList:
		devicesList(mgmt, *enterpriseID)
	case cmdDevicesDelete:
		devicesDelete(mgmt, *enterpriseID, *deviceID)
	case cmdDevicesRelinquish:
		devicesRelinquishOwnership(mgmt, *enterpriseID, *deviceID)
	default:
		log.Fatalf("Unknown command: %s", *command)
	}

}

func enterprisesDelete(mgmt *androidmanagement.Service, enterpriseID string) {
	if enterpriseID == "" {
		log.Fatalf("enterprise_id must be set")
	}
	_, err := mgmt.Enterprises.Delete("enterprises/" + enterpriseID).Do()
	if err != nil {
		log.Fatalf("Error deleting enterprise: %v", err)
	}
}

func enterprisesList(mgmt *androidmanagement.Service) {
	enterprises, err := mgmt.Enterprises.List().ProjectId(androidProjectID).Do()
	if err != nil {
		log.Fatalf("Error listing enterprises: %v", err)
	}
	if len(enterprises.Enterprises) == 0 {
		log.Printf("No enterprises found")
		return
	}
	for _, enterprise := range enterprises.Enterprises {
		log.Printf("Enterprise: %+v", *enterprise)
	}
}

func policiesList(mgmt *androidmanagement.Service, enterpriseID string) {
	if enterpriseID == "" {
		log.Fatalf("enterprise_id must be set")
	}
	result, err := mgmt.Enterprises.Policies.List("enterprises/" + enterpriseID).Do()
	if err != nil {
		log.Fatalf("Error listing policies: %v", err)
	}
	if len(result.Policies) == 0 {
		log.Printf("No policies found")
		return
	}
	b, err := json.Marshal(result.Policies, jsontext.WithIndent("  "))
	if err != nil {
		log.Fatalf("Error marshalling policies: %v", err)
	}
	fmt.Println(string(b))
}

func applicationsGet(mgmt *androidmanagement.Service, enterpriseID string, applicationID string) {
	if enterpriseID == "" {
		log.Fatalf("enterprise_id must be set")
	}
	if applicationID == "" {
		log.Fatal("application ID argument missing")
	}
	result, err := mgmt.Enterprises.Applications.Get(fmt.Sprintf("enterprises/%s/applications/%s", enterpriseID, applicationID)).Do()
	if err != nil {
		log.Fatalf("Error getting application: %v", err)
	}
	b, err := json.Marshal(result, jsontext.WithIndent("  "))
	if err != nil {
		log.Fatalf("Error marshalling application: %v", err)
	}
	fmt.Println(string(b))
}

func policiesDelete(mgmt *androidmanagement.Service, enterpriseID, policyID string) {
	if enterpriseID == "" || policyID == "" {
		log.Fatalf("enterprise_id and policy_id must be set")
	}
	_, err := mgmt.Enterprises.Policies.Delete("enterprises/" + enterpriseID + "/policies/" + policyID).Do()
	if err != nil {
		log.Fatalf("Error deleting policy: %v", err)
	}
	log.Printf("Policy %s deleted", policyID)
}

func devicesList(mgmt *androidmanagement.Service, enterpriseID string) {
	if enterpriseID == "" {
		log.Fatalf("enterprise_id must be set")
	}
	result, err := mgmt.Enterprises.Devices.List("enterprises/" + enterpriseID).Do()
	if err != nil {
		log.Fatalf("Error listing devices: %v", err)
	}
	if len(result.Devices) == 0 {
		log.Printf("No policies found")
		return
	}
	b, err := json.Marshal(result.Devices, jsontext.WithIndent("  "))
	if err != nil {
		log.Fatalf("Error marshalling devices: %v", err)
	}
	fmt.Println(string(b))
	log.Printf("Total devices: %d", len(result.Devices))
}

func devicesDelete(mgmt *androidmanagement.Service, enterpriseID string, deviceID string) {
	if enterpriseID == "" || deviceID == "" {
		log.Fatalf("enterprise_id and device_id must be set")
	}
	_, err := mgmt.Enterprises.Devices.Delete("enterprises/" + enterpriseID + "/devices/" + deviceID).Do()
	if err != nil {
		log.Fatalf("Error listing devices: %v", err)
	}
	log.Printf("Device %s deleted", deviceID)
}

func devicesRelinquishOwnership(mgmt *androidmanagement.Service, enterpriseID, deviceID string) {
	if enterpriseID == "" || deviceID == "" {
		log.Fatalf("enterprise_id and device_id must be set")
	}
	operation, err := mgmt.Enterprises.Devices.IssueCommand("enterprises/"+enterpriseID+"/devices/"+deviceID, &androidmanagement.Command{
		Type: "RELINQUISH_OWNERSHIP",
	}).Do()
	if err != nil {
		log.Fatalf("Error issuing command: %v", err)
	}
	data, err := json.Marshal(operation, jsontext.WithIndent("  "))
	if err != nil {
		log.Fatalf("Error marshalling operation: %v", err)
	}
	log.Println(string(data))
}

func enterprisesWebTokensCreate(mgmt *androidmanagement.Service, enterpriseID string) {
	if enterpriseID == "" {
		log.Fatalf("enterprise_id must be set")
	}

	webToken := &androidmanagement.WebToken{
		ParentFrameUrl: "https://example.com",
		Permissions:    []string{"APPROVE_APPS"},
	}

	result, err := mgmt.Enterprises.WebTokens.Create("enterprises/"+enterpriseID, webToken).Do()
	if err != nil {
		log.Fatalf("Error creating web token: %v", err)
	}

	data, err := json.Marshal(result, jsontext.WithIndent("  "))
	if err != nil {
		log.Fatalf("Error marshalling web token: %v", err)
	}
	log.Println(string(data))

	// Construct and display the complete URL
	if result.Value != "" {
		playURL := "https://play.google.com/work/embedded/search?token=" + result.Value
		log.Printf("\nComplete Play Store URL:\n%s\n", playURL)
	}
}
